from typing import Optional
from fastapi import APIRouter, HTTPException, status, Depends
from pydantic import BaseModel
from database import get_db
from datetime import datetime

router = APIRouter(
    prefix="/trades",
    tags=["trades"],
)


class DepositRequest(BaseModel):
    cardID: str
    amount: float
    remark: Optional[str] = None


class WithdrawRequest(BaseModel):
    cardID: str
    amount: float
    password: str
    remark: Optional[str] = None


class TransferRequest(BaseModel):
    from_cardID: str
    to_cardID: str
    amount: float
    password: str
    remark: Optional[str] = None


class TradeUpdateRequest(BaseModel):
    cardID: str
    tradeDate: datetime
    tradeType: str
    tradeMoney: float
    remark: Optional[str] = None


class TradeCreateRequest(BaseModel):
    cardID: str
    tradeDate: Optional[datetime] = None
    tradeType: str
    tradeMoney: float
    remark: Optional[str] = None


@router.post("/deposit", status_code=status.HTTP_200_OK)
def deposit(request: DepositRequest, conn=Depends(get_db)):
    cursor = conn.cursor()
    try:
        args = [request.cardID, request.amount, request.remark, 0, ""]
        result_args = cursor.callproc("sp_Deposit", args)

        result_code = result_args[3]
        result_message = result_args[4]

        if result_code == 0:
            conn.commit()
            return {"message": result_message}
        else:
            conn.rollback()
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST, detail=result_message
            )
    except Exception as e:
        conn.rollback()
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
        )
    finally:
        cursor.close()


@router.post("/add", status_code=status.HTTP_201_CREATED)
def add_trade(request: TradeCreateRequest, conn=Depends(get_db)):
    cursor = conn.cursor(dictionary=True)
    try:
        args = (
            request.cardID,
            request.tradeDate,
            request.tradeType,
            request.tradeMoney,
            request.remark,
        )
        cursor.callproc("sp_AddTrade", args)
        conn.commit()

        cursor.execute("SELECT LAST_INSERT_ID() as id")
        last_id_result = cursor.fetchone()
        if not last_id_result:
            raise HTTPException(
                status_code=500, detail="Could not retrieve last insert ID."
            )

        last_id = last_id_result["id"]

        cursor.callproc("sp_GetTradeById", (last_id,))
        for result in cursor.stored_results():
            new_trade = result.fetchone()

        if new_trade is None:
            raise HTTPException(status_code=404, detail="Trade not found after creation")

        return new_trade
    except Exception as e:
        conn.rollback()
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
        )
    finally:
        cursor.close()


@router.put("/{trade_id}")
def update_trade(trade_id: int, request: TradeUpdateRequest, conn=Depends(get_db)):
    cursor = conn.cursor(dictionary=True)
    try:
        args = (
            trade_id,
            request.cardID,
            request.tradeDate,
            request.tradeType,
            request.tradeMoney,
            request.remark,
        )
        cursor.callproc("sp_UpdateTrade", args)
        conn.commit()

        cursor.callproc("sp_GetTradeById", (trade_id,))
        for result in cursor.stored_results():
            updated_trade = result.fetchone()

        if updated_trade is None:
            raise HTTPException(status_code=404, detail="Trade not found")

        return updated_trade
    except Exception as e:
        conn.rollback()
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
        )
    finally:
        cursor.close()


@router.post("/withdraw", status_code=status.HTTP_200_OK)
def withdraw(request: WithdrawRequest, conn=Depends(get_db)):
    cursor = conn.cursor()
    try:
        args = [request.cardID, request.amount, request.password, request.remark, 0, ""]
        result_args = cursor.callproc("sp_Withdraw", args)

        result_code = result_args[4]
        result_message = result_args[5]

        if result_code == 0:
            conn.commit()
            return {"message": result_message}
        else:
            conn.rollback()
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST, detail=result_message
            )
    except Exception as e:
        conn.rollback()
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
        )
    finally:
        cursor.close()


@router.post("/transfer", status_code=status.HTTP_200_OK)
def transfer(request: TransferRequest, conn=Depends(get_db)):
    cursor = conn.cursor()
    try:
        args = [
            request.from_cardID,
            request.to_cardID,
            request.amount,
            request.password,
            request.remark,
            0,
            "",
        ]
        result_args = cursor.callproc("sp_TransferMoney", args)

        result_code = result_args[5]
        result_message = result_args[6]

        if result_code == 0:
            conn.commit()
            return {"message": result_message}
        else:
            conn.rollback()
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST, detail=result_message
            )
    except Exception as e:
        conn.rollback()
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
        )
    finally:
        cursor.close()


@router.delete("/{trade_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_trade(trade_id: int, conn=Depends(get_db)):
    cursor = conn.cursor()
    try:
        cursor.callproc("sp_DeleteTrade", (trade_id,))
        conn.commit()
    except Exception as e:
        conn.rollback()
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
    finally:
        cursor.close()


@router.get("/by-card/{card_id}")
def get_trades_by_card_id(card_id: str, conn=Depends(get_db)):
    cursor = conn.cursor(dictionary=True)
    try:
        cursor.callproc("sp_GetTradesByCardId", (card_id,))
        for result in cursor.stored_results():
            trades = result.fetchall()
        return trades
    finally:
        cursor.close()


@router.get("/")
def get_all_trades(conn=Depends(get_db)):
    cursor = conn.cursor(dictionary=True)
    try:
        cursor.callproc("sp_GetAllTrades")
        for result in cursor.stored_results():
            trades = result.fetchall()
        return trades
    finally:
        cursor.close()
